﻿#include <fstream>
#include <sstream>
#include <set>
#include <queue>
#include <numeric>
#include <filesystem>
#include "Strategies.h"
#include "AIPredictor.h"

template<typename Task>
class ParallelTaskArray
{
public:
    ParallelTaskArray(std::vector<std::shared_ptr<Task>>& in_tasks, bool in_bEnableLog = false) :
    tasks(in_tasks),
    threadNum(std::thread::hardware_concurrency()),
    bEnableLog(in_bEnableLog)
    {
        idxCounter.store(0);
    }

    ~ParallelTaskArray() {}

public:
    void Execute()
    {
        TimeCounter timeCounter;

        for (size_t i = 0; i < threadNum; i++)
        {
            threads.emplace_back
            (
                [this]()
                {
                    while (auto task = this->AcquireNextTask())
                    {
                        (*task)();
                    }
                }
            );
        }

        if (bEnableLog)
        {
            uint32_t processedCount = 0;
            uint32_t totalCount = 0;
            do
            {
                GetProgress(processedCount, totalCount);
                std::cout << "Processing: " << processedCount << "/" << totalCount << std::endl;
                std::this_thread::sleep_for(std::chrono::seconds(1));
            } while (processedCount < totalCount);
        }

        for (auto& it : threads)
        {
            it.join();
        }
        std::cout << "Processed finished!" << std::endl;
    }
private:
    std::vector<std::shared_ptr<Task>> tasks;
    std::vector<std::thread> threads;
    uint32_t threadNum = 1;
    std::atomic_uint32_t idxCounter;
    bool bEnableLog;
    void GetProgress(uint32_t& out_processedCount, uint32_t& out_totalCount)
    {
        uint32_t processedCount = std::min<uint32_t>(idxCounter.load(), tasks.size());
        out_processedCount = processedCount;
        out_totalCount = tasks.size();
    }

    std::shared_ptr<Task> AcquireNextTask()
    {
        uint32_t nextTaskIdx = idxCounter.fetch_add(1);
        return nextTaskIdx < tasks.size() ? tasks[nextTaskIdx] : nullptr;
    }
};

std::vector<std::vector<std::string>> readCSV(const std::string& filename, bool bSkipFirstLine)
{
    std::vector<std::vector<std::string>> data;
    std::ifstream file(filename);
    std::string line;

    if (!file.is_open()) {
        std::cerr << "Can not find file: " << filename << std::endl;
        return data;
    }

    while (getline(file, line))
    {
        std::vector<std::string> row;
        std::stringstream lineStream(line);
        std::string cell;
        bool bInQuotes = false;

        for (const auto& c : line)
        {
            if (c == '"')
            {
                bInQuotes = !bInQuotes;
            }
            else if (c == ',' && !bInQuotes)
            {
                row.push_back(cell);
                cell.clear();
            }
            else if(c != ',')
            {
                cell.push_back(c);
            }
        }

        if (line.back() != ',')
        {
            row.push_back(cell);
        }
        cell.clear();

        if (bSkipFirstLine)
        {
            bSkipFirstLine = !bSkipFirstLine;
        }
        else
        {
            data.push_back(row);
        }
    }

    file.close();
    return data;
}

void GetAllSubjectsData(const std::vector<std::string>& paths, std::vector<std::shared_ptr<Subject>>& subjects_sptr)
{
    for (const auto& it : paths)
    {
        subjects_sptr.push_back(std::make_shared<Subject>());
    }

    std::vector<std::vector<std::vector<std::string>>> allCsvData;
    allCsvData.resize(paths.size());
    for (size_t i = 0; i < paths.size(); i++)
    {
        const auto& filePath = paths[i];
        allCsvData[i] = readCSV(filePath, true);
    }
    std::vector<std::set<std::string>> keySet;
    keySet.resize(paths.size());
    for (size_t i = 0; i < paths.size(); i++)
    {
        auto& csv = allCsvData[i];
        for (size_t j = 0; j < csv.size(); j++)
        {
            keySet[i].insert(csv[j][0]);
        }
    }

    for (size_t i = 0; i < paths.size(); i++)
    {
        auto& csv = allCsvData[i];
        for (int32_t idx = csv.size() - 1; idx >= 0; idx--)
        {
            const std::vector<std::string>& currentData = csv[idx];
            double closedPrice = std::stod(currentData[1]);
            double startPrice = std::stod(currentData[2]);
            double highPrice = std::stod(currentData[3]);
            double lowPrice = std::stod(currentData[4]);

            double tradedNum = std::stod(currentData[5]);
            //double diffRate = std::stod(currentData[6]);

            bool bBoth = true;
            for (const auto& csvIt : keySet)
            {
                bBoth &= csvIt.find(currentData[0]) != csvIt.end();
            }
            if (bBoth)
            {
               subjects_sptr[i]->closedPrices.push_back(closedPrice);
               subjects_sptr[i]->startPrices.push_back(startPrice);
               subjects_sptr[i]->highPrices.push_back(highPrice);
               subjects_sptr[i]->lowPrices.push_back(lowPrice);
               subjects_sptr[i]->tradedNum.push_back(tradedNum);
               //subjects_sptr[i]->diffRate.push_back(diffRate);

            }
        }
        subjects_sptr[i]->Initialize();
    }
}

void GetAllAutoSubjectsData(const std::vector<std::string>& paths, std::vector<std::shared_ptr<Subject>>& subjects_sptr)
{
    std::vector<std::shared_ptr<std::vector<std::vector<std::string>>>> allCsvData;
    {
        struct TestTask
        {
            TestTask(std::shared_ptr<std::vector<std::vector<std::string>>> in_csv_sptr) : csv_sptr(in_csv_sptr) {}
            std::shared_ptr<std::vector<std::vector<std::string>>> csv_sptr;
            std::string path;
            void operator()()
            {
                *csv_sptr = readCSV(path, true);
            }
        };
        std::vector<std::shared_ptr<TestTask>> allTasks;
        for (auto it : paths)
        {
            auto csv_sptr = std::make_shared<std::vector<std::vector<std::string>>>();
            auto task = std::make_shared<TestTask>(csv_sptr);
            task->path = it;
            allTasks.push_back(task);
            allCsvData.push_back(csv_sptr);
        }
        ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
        parallelTaskArray.Execute();
    }

    {
        struct TestTask
        {
            std::shared_ptr<std::vector<std::vector<std::string>>> csv;
            std::shared_ptr<Subject> subjects_sptr;
            void operator()()
            {
                for (int32_t idx = 0; idx < csv->size(); idx++)
                {
                    const std::vector<std::string>& currentData = (*csv)[idx];
                    uint32_t id = std::stoi(currentData[1]);
                    double startPrice = std::stod(currentData[2]);
                    double closedPrice = std::stod(currentData[3]);
                    double highPrice = std::stod(currentData[4]);
                    double lowPrice = std::stod(currentData[5]);

                    double tradedNum = std::stod(currentData[6]);
                    double tradedQuant = std::stod(currentData[7]);
                    double diffRate = std::stod(currentData[8]);
                    double yieldRate = std::stod(currentData[9]);
                    double yieldQuant = std::stod(currentData[10]);
                    //double changedRate = std::stod(currentData[11]);

                    //bool bBoth = false;
                    //for (const auto& csvIt : keySet)
                    //{
                    //    bBoth &= csvIt.find(currentData[0]) != csvIt.end();
                    //}
                    //if (bBoth)
                    {
                        subjects_sptr->id = id;
                        subjects_sptr->closedPrices.push_back(closedPrice);
                        subjects_sptr->startPrices.push_back(startPrice);
                        subjects_sptr->highPrices.push_back(highPrice);
                        subjects_sptr->lowPrices.push_back(lowPrice);


                        subjects_sptr->tradedNum.push_back(tradedNum);
                        subjects_sptr->tradedQuant.push_back(tradedQuant);
                        subjects_sptr->diffRate.push_back(diffRate);
                        subjects_sptr->yieldRate.push_back(yieldRate);
                        subjects_sptr->yieldQuant.push_back(yieldQuant);
                        //subjects_sptr->changedRate.push_back(changedRate);

                    }
                }
                subjects_sptr->Initialize();
            }
        };
        std::vector<std::shared_ptr<TestTask>> allTasks;

        for (uint32_t i = 0; i < paths.size(); i++)
        {
            auto task = std::make_shared<TestTask>();
            auto subject = std::make_shared<Subject>();
            task->subjects_sptr = subject;
            task->csv = allCsvData[i];
            allTasks.push_back(task);
            subjects_sptr.push_back(subject);
        }
        ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
        parallelTaskArray.Execute();
    }
}

void GetAllMinSubjectsData(const std::vector<std::string>& paths, std::vector<std::shared_ptr<Subject>>& subjects_sptr)
{
    std::vector<std::shared_ptr<std::vector<std::vector<std::string>>>> allCsvData;
    {
        struct TestTask
        {
            TestTask(std::shared_ptr<std::vector<std::vector<std::string>>> in_csv_sptr) : csv_sptr(in_csv_sptr) {}
            std::shared_ptr<std::vector<std::vector<std::string>>> csv_sptr;
            std::string path;
            void operator()()
            {
                *csv_sptr = readCSV(path, true);
            }
        };
        std::vector<std::shared_ptr<TestTask>> allTasks;
        for (auto it : paths)
        {
            auto csv_sptr = std::make_shared<std::vector<std::vector<std::string>>>();
            auto task = std::make_shared<TestTask>(csv_sptr);
            task->path = it;
            allTasks.push_back(task);
            allCsvData.push_back(csv_sptr);
        }
        ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
        parallelTaskArray.Execute();
    }

    {
        struct TestTask
        {
            std::shared_ptr<std::vector<std::vector<std::string>>> csv;
            std::shared_ptr<Subject> subjects_sptr;
            void operator()()
            {
                for (int32_t idx = 0; idx < csv->size(); idx++)
                {
                    const std::vector<std::string>& currentData = (*csv)[idx];
                    uint32_t id = std::stoi(currentData[1]);
                    double meanPrice = std::stod(currentData[7]);
                    {
                        subjects_sptr->meanPrices.push_back(meanPrice);
                    }
                }
                subjects_sptr->Initialize();
            }
        };
        std::vector<std::shared_ptr<TestTask>> allTasks;

        for (uint32_t i = 0; i < paths.size(); i++)
        {
            auto task = std::make_shared<TestTask>();
            auto subject = std::make_shared<Subject>();
            task->subjects_sptr = subject;
            task->csv = allCsvData[i];
            allTasks.push_back(task);
            subjects_sptr.push_back(subject);
        }
        ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
        parallelTaskArray.Execute();
    }
}

void WriteXiaoQiuResult(std::string& path, std::string& content)
{
    std::ofstream outFile(path);

    if (!outFile.is_open()) {
        std::cerr << "Failed!" << std::endl;
        return;
    }
    outFile << content << std::endl;

    outFile.close();
}

double compute_std(const std::vector<double>& data)
{
    if (data.empty()) return 0.0;
    double mean = 0.0;
    for (double x : data)
    {
        mean += x;
    }
    mean /= data.size();
    double variance = 0.0;
    for (double x : data)
    {
        variance += (x - mean) * (x - mean);
    }
    variance /= data.size();
    return std::sqrt(variance);
}

std::vector<double> smooth_data(const std::vector<double>& data, int window_size)
{
    std::vector<double> smoothed;
    int n = data.size();
    if (n == 0) return smoothed;
    window_size = std::max(1, window_size); // 确保窗口大小至少为1
    int half_window = window_size / 2;
    for (int i = 0; i < n; ++i)
    {
        int start = std::max(0, i - half_window);
        int end = std::min(n - 1, i + half_window);
        double sum = 0.0;
        int count = 0;
        for (int j = start; j <= end; ++j)
        {
            sum += data[j];
            count++;
        }
        smoothed.push_back(sum / count);
    }
    return smoothed;
}

std::vector<int> find_inflection_points(const std::vector<double>& data, int smooth_window = 3, double threshold_ratio = 0.0)
{
    std::vector<int> inflection_points;
    if (data.size() < 3) return inflection_points; // 数据不足

    // 数据平滑
    std::vector<double> smoothed = smooth_data(data, smooth_window);
    double std_dev = compute_std(smoothed);
    double dynamic_threshold = threshold_ratio * std_dev;

    // 计算一阶差分
    std::vector<double> diff;
    for (size_t i = 0; i < smoothed.size() - 1; ++i)
    {
        diff.push_back(smoothed[i + 1] - smoothed[i]);
    }

    // 检测极值点并筛选
    for (size_t i = 1; i < smoothed.size() - 1; ++i)
    {
        double left_diff = diff[i - 1];
        double right_diff = diff[i];
        bool is_max = (left_diff > 0 && right_diff < 0);
        bool is_min = (left_diff < 0 && right_diff > 0);

        if (is_max || is_min)
        {
            // 计算周围点的平均值（排除当前点）
            int m = 2; // 左右各取2个点
            int start = std::max(0, static_cast<int>(i) - m);
            int end = std::min(static_cast<int>(smoothed.size()) - 1, static_cast<int>(i) + m);
            double sum = 0.0;
            int count = 0;
            for (int j = start; j <= end; ++j)
            {
                if (j != static_cast<int>(i))
                {
                    sum += smoothed[j];
                    count++;
                }
            }
            if (count == 0)
            {
                inflection_points.push_back(i);
                continue;
            }
            double avg = sum / count;
            double diff_value = smoothed[i] - avg;

            // 判断差异是否超过动态阈值
            if (std::abs(diff_value) >= dynamic_threshold)
            {
                inflection_points.push_back(i);
            }
        }
    }
    return inflection_points;
}

int main()
{
    // Heng Sheng Ke Ji Test
    if (true)
    {
        TimeCounter timeCounter;
        std::vector<std::string> filePaths;
        std::vector<std::shared_ptr<Subject>> subjects_sptr;
        std::string test("1min_513260.csv");
        for (const auto& entry : std::filesystem::directory_iterator("../../StockData/stock_data"))
        {
            std::string filePath = entry.path().string();
            if (filePath.substr(filePath.length() - test.length(), test.length()) == test)
            {
                filePaths.push_back(entry.path().string());
            }
        }
        GetAllMinSubjectsData(filePaths, subjects_sptr);

        for (auto it : subjects_sptr)
        {
            std::cout << "Begin" << std::endl;
            auto& idxs = find_inflection_points(it->meanPrices);
            for (const auto& it : idxs)
            {
                std::cout << it << std::endl;
            }
            std::cout << "Close" << std::endl;
        }
    }

    // xiao qiu choose shares~
    if(false)
    {
        TimeCounter timeCounter;
        std::vector<std::string> filePaths;
        for (const auto& entry : std::filesystem::directory_iterator("../Resource/stock_data"))
        {
            filePaths.push_back(entry.path().string());
        }

        std::vector<std::shared_ptr<Subject>> subjects_sptr;;
        GetAllAutoSubjectsData(filePaths, subjects_sptr);
        
        struct TestTask
        {
            TestTask(std::shared_ptr<Subject> in_subject_sptr) : subject_sptr(in_subject_sptr) {}
            std::shared_ptr<Subject> subject_sptr;
            bool bResult = false;
            void operator()()
            {
                auto& startPrices = subject_sptr->startPrices;
                auto& closedPrices = subject_sptr->closedPrices;
                auto& highPrices = subject_sptr->highPrices;
                auto& lowPrices = subject_sptr->lowPrices;
                uint32_t length = startPrices.size();

                if (length > 0)
                {
                    uint32_t failedIdx = length;
                    for (int32_t i = length - 1; i >= length - 20; i--)
                    {
                        auto startPrice = startPrices[i];
                        auto closedPrice = closedPrices[i];
                        auto highPrice = highPrices[i];
                        auto lowPrice = lowPrices[i];

                        if ((highPrice - startPrice) / startPrice < 0.1 && (closedPrice - startPrice) / startPrice < 0.09)
                        {
                            failedIdx = i;
                            break;
                        }

                    }

                    uint32_t bigDrawdownIdx = length;
                    for (uint32_t i = failedIdx; i < length; i++)
                    {
                        auto startPrice = startPrices[i];
                        auto closedPrice = closedPrices[i];
                        auto highPrice = highPrices[i];
                        auto lowPrice = lowPrices[i];

                        if ((closedPrice - startPrice) / startPrice > 0.04)
                        {
                            bigDrawdownIdx = i;
                            break;
                        }
                    }

                    if (bigDrawdownIdx < length)
                    {
                        auto lastClosePrice = closedPrices.back();
                        auto bigDrawdownPrice = closedPrices[bigDrawdownIdx];
                        bResult = lastClosePrice > bigDrawdownPrice;
                    }

                }
            }
        };

        std::vector<std::shared_ptr<TestTask>> allTasks;
        for (size_t i = 0; i < subjects_sptr.size(); i++)
        {
            allTasks.push_back(std::make_shared<TestTask>(subjects_sptr[i]));
        }

        ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
        parallelTaskArray.Execute();

        std::vector<std::shared_ptr<Subject>> filteredShares;
        
        for (auto& it : allTasks)
        {
            if (it->bResult)
            {
                filteredShares.push_back(it->subject_sptr);
            }
        }

        std::string XiaoQiuResult;
        for (const auto& it : filteredShares)
        {
            XiaoQiuResult.append(std::to_string(it->id));
            XiaoQiuResult.append(",\n");
        }
        std::string path{ "../Results/XiaoQiuResult.txt" };
        WriteXiaoQiuResult(path, XiaoQiuResult);
        std::cout << "XiaoQiuResult has write successfuly, ../Result/XiaoQiuResult.txt" << std::endl;
    }







    if (false)
    {
        std::vector<std::string> filePaths =
        {
            //"../Resource/Gold_2015_1_1_2025_1_1.csv",
            "../Resource/IXIC_2015_1_1_2025_1_1.csv",
            //"../Resource/KC50_2024_3_15_2025_3_15.csv",
        };

        std::vector<std::shared_ptr<Subject>> subjects_sptr;;
        GetAllSubjectsData(filePaths, subjects_sptr);

        //std::vector<std::string> filePaths;
        //for (const auto& entry : std::filesystem::directory_iterator("../Resource/stock_data"))
        //{
        //    filePaths.push_back(entry.path().string());
        //}

        //std::vector<std::shared_ptr<Subject>> subjects_sptr;;
        //GetAllAutoSubjectsData(filePaths, subjects_sptr);

        const std::string trainDataPath = "../Resource/TrainData/";
        bool bExistPath = std::filesystem::exists(trainDataPath);

        std::vector<std::shared_ptr<AIPredictor>> predictors;
        if (false)
        {

            for (const auto& entry : std::filesystem::directory_iterator(trainDataPath))
            {
                if (entry.path().extension() == ".bin")
                {
                    auto predictor = std::make_shared<AIPredictor>();
                    predictor->load(entry.path().string());
                    predictors.push_back(predictor);
                }
            }
        }
        else
        {
            struct TestTask
            {
                std::shared_ptr<AIPredictor> predictor;
                std::vector<std::shared_ptr<Subject>>* subjects_sptr_ptr = nullptr;

                void operator()()
                {
                    auto& subjects_sptr = *subjects_sptr_ptr;
                    uint32_t stockCount = subjects_sptr.size();

                    std::vector<std::vector<double>> train_features;
                    std::vector<int> train_labels;
                    predictor->prepare_data(subjects_sptr, train_features, train_labels);

                    predictor->normalize(train_features);

                    auto start = std::chrono::high_resolution_clock::now();
                    predictor->train(train_features, train_labels);
                    auto end = std::chrono::high_resolution_clock::now();
                    std::chrono::duration<double> elapsed = end - start;
                    //std::cout << "Training completed in " << elapsed.count() << " seconds\n";
                }
            };

            std::vector<std::shared_ptr<TestTask>> allTasks;
            for (size_t i = 20; i <= 20; i++)
            {
                //for (size_t j = 2; j <= i - 2; j++)
                for (size_t j = 2; j <= 10; j++)
                {
                    auto task = std::make_shared<TestTask>();
                    auto predictor = std::make_shared<AIPredictor>();
                    predictor->daysLearn = i;
                    predictor->daysLast = j;
                    task->predictor = predictor;
                    task->subjects_sptr_ptr = &subjects_sptr;
                    allTasks.push_back(task);
                    predictors.push_back(predictor);
                }
            }

            ParallelTaskArray<TestTask> parallelTaskArray(allTasks, true);
            parallelTaskArray.Execute();

            std::filesystem::create_directories(trainDataPath);
            for (uint32_t i = 0; i < predictors.size(); i++)
            {
                std::string path;
                path.append(trainDataPath);
                path.append(std::to_string(i));
                path.append(".bin");
                predictors[i]->save(path);
            }
        }

        // Test all predictor.
        {
            struct TestTask
            {
                double acceurace = 0.0;
                uint32_t result_hitCount = 0;
                uint32_t result_hitCount_0 = 0;
                uint32_t result_hitCount_1 = 0;
                uint32_t result_totalCount = 0;
                std::shared_ptr<AIPredictor> predictor;
                std::vector<std::shared_ptr<Subject>>* subjects_sptr_ptr = nullptr;
                void operator()()
                {
                    auto& subjects_sptr = *subjects_sptr_ptr;

                    uint32_t totalCount = 0;
                    uint32_t hitCount = 0;
                    uint32_t hitCount_0 = 0;
                    uint32_t hitCount_1 = 0;
                    uint32_t inputDays = predictor->daysLearn - predictor->daysLast;
                    for (auto& subjectIt : subjects_sptr)
                    {
                        uint32_t subjectLength = subjectIt->closedPrices.size();
                        if (subjectLength < predictor->daysLearn)
                        {
                            continue;
                        }
                        for (uint32_t i = 0; i < subjectLength - predictor->daysLearn; i++)
                        {
                            uint32_t j = 0;
                            std::vector<double> input;

                            auto startPrices0 = subjectIt->startPrices[i];
                            auto closedPrices0 = subjectIt->closedPrices[i];
                            auto highPrices0 = subjectIt->highPrices[i];
                            auto lowPrices0 = subjectIt->lowPrices[i];
                            auto tradedNum0 = subjectIt->tradedNum[i];

                            for (j = i; j < i + inputDays; j++)
                            {
                                input.push_back(subjectIt->startPrices[j] / startPrices0);
                                input.push_back(subjectIt->closedPrices[j] / closedPrices0);
                                input.push_back(subjectIt->highPrices[j] / highPrices0);
                                input.push_back(subjectIt->lowPrices[j] / lowPrices0);
                                input.push_back(subjectIt->tradedNum[j] / tradedNum0);
                            }
                            totalCount++;
                            auto result = predictor->predict(input);
                            auto dataLast2Idx = j + 0;
                            auto dataLast1Idx = j + predictor->daysLast - 1;
                            auto last2High = subjectIt->highPrices[dataLast2Idx];
                            auto last1Open = subjectIt->startPrices[dataLast1Idx];
                            double incRate = (last1Open - last2High) / last2High;
                            //double labelShouldBe = std::round(incRate * 100.0);
                            int labelShouldBe = incRate > 0.0 ? 1 : 0;

                            if (labelShouldBe == result)
                            {
                                hitCount++;
                                if (result == 0)
                                {
                                    hitCount_0++;
                                }
                                else
                                {
                                    hitCount_1++;
                                }
                            }
                        }
                    }
                    acceurace = double(hitCount) / double(totalCount);
                    result_hitCount = hitCount;
                    result_hitCount_0 = hitCount_0;
                    result_hitCount_1 = hitCount_1;
                    result_totalCount = totalCount;
                }
            };

            std::vector<std::shared_ptr<TestTask>> allTasks;
            for (auto& it : predictors)
            {
                auto task = std::make_shared<TestTask>();
                task->predictor = it;
                task->subjects_sptr_ptr = &subjects_sptr;
                allTasks.push_back(task);
            }
            ParallelTaskArray<TestTask> parallelTaskArray(allTasks);
            parallelTaskArray.Execute();

            auto maxAccTask = std::max_element(allTasks.begin(), allTasks.end(), [](std::shared_ptr<TestTask> a, std::shared_ptr<TestTask> b) {return b->acceurace > a->acceurace; });
            auto maxHit0Task = std::max_element(allTasks.begin(), allTasks.end(), [](std::shared_ptr<TestTask> a, std::shared_ptr<TestTask> b) {return b->result_hitCount_0 > a->result_hitCount_0; });
            auto maxHit1Task = std::max_element(allTasks.begin(), allTasks.end(), [](std::shared_ptr<TestTask> a, std::shared_ptr<TestTask> b) {return b->result_hitCount_1 > a->result_hitCount_1; });

            std::vector<std::shared_ptr<TestTask>> filteredTasks = { *maxAccTask , *maxHit0Task , *maxHit1Task };

            auto dumpData = 
            [](std::shared_ptr<TestTask> task)
            {
                std::cout << "daysLearn is " << task->predictor->daysLearn << " daysLast is " << task->predictor->daysLast << std::endl;
                std::cout << "hitCount is " << task->result_hitCount << std::endl;
                std::cout << "hitCount0 is " << task->result_hitCount_0 << std::endl;
                std::cout << "hitCount1 is " << task->result_hitCount_1 << std::endl;
            };

            std::cout << "Max accuracy is " << (*maxAccTask)->acceurace << std::endl;
            dumpData(*maxAccTask);

            std::cout << "Max hitcount0 is " << (*maxHit0Task)->result_hitCount_0 << std::endl;
            dumpData(*maxHit0Task);

            std::cout << "Max hitcount1 is " << (*maxHit1Task)->result_hitCount_1 << std::endl;
            dumpData(*maxHit1Task);

            std::cout << "Start Dump All Task Results." << std::endl;
            for (uint32_t i = 0; i < allTasks.size(); i++)
            {
                auto& task = allTasks[i];
                std::cout << "task[" << i << "] hitCount is " << task->result_hitCount << " hitCount0 is " << task->result_hitCount_0 << " hitCount1 is " << task->result_hitCount_1 << " daysLearn is " << task->predictor->daysLearn << " daysLast is " << task->predictor->daysLast << " accuracy is " << task->acceurace << std::endl;

            }
            std::cout << "Close Dump All Task Results." << std::endl;
        }

    }

    if (false)
    {
        // Read csv and prepare history price.
    //std::string filename = "../Resource/TQQQ_10year.csv";
    //std::string filename = "../Resource/Nasdaq_10year.csv";
    //std::vector<std::vector<std::string>> csvData = readCSV(filename, true);

    //double currentETFPrice = 173;
    //double currentNasdqPrice = std::stod(csvData[0][1]);
    //double scale = currentETFPrice / currentNasdqPrice;
    //std::vector<double> hisrotyPrices;

    //for (int32_t idx = csvData.size() - 1; idx >= 0; idx--)
    //{
    //    const std::vector<std::string>& currentData = csvData[idx];
    //    double currentPrice = std::stod(currentData[1]) * scale;
    //    hisrotyPrices.push_back(currentPrice);
    //}

        std::vector<std::string> filePaths =
        {
            //"../Resource/Gold_2015_1_1_2025_1_1.csv",
            "../Resource/IXIC_2015_1_1_2025_1_1.csv",
            //"../Resource/KC50_2024_3_15_2025_3_15.csv",
        };

        std::vector<std::shared_ptr<Subject>> subjects_sptr;;
        GetAllSubjectsData(filePaths, subjects_sptr);



        if (false)
        {
            uint32_t subjectCount = subjects_sptr.size();
            uint32_t subjectLength = subjects_sptr[0]->closedPrices.size();

            // Prepare some basic parameter.
            double startCash = 750000.0;
            double everyDayCash = 1000;

            double extraCashStride = 1.0;
            double minExtraCash = 100.0;
            double maxExtraCash = 100000.0;
            const uint32_t totalExtCount = std::floor((maxExtraCash - minExtraCash) / extraCashStride) + 1;

            double stopProfitStride = 0.01;
            double minStopProfit = 99999;
            double maxStopProfit = 99999;
            const uint32_t totalStopCount = std::floor((maxStopProfit - minStopProfit) / stopProfitStride) + 1;

            // Prepare all tasks.
            std::vector<std::shared_ptr<BaseStrategy>> strategies;

            //{
            //    BaseStrategy::Param baseParam{};
            //    baseParam.startCash = startCash;
            //    baseParam.subjects = subjects_sptr;
            //    baseParam.subjectCount = subjectCount;
            //    baseParam.subjectLength = subjectLength;

            //    MAStrategy::Param maGreedParam{};
            //    maGreedParam.everyDayCash = 1000.0;
            //    auto& localStrategy = std::make_shared<MAStrategy>(baseParam, maGreedParam);
            //    localStrategy->Reset();
            //    localStrategy->SetRecord(false);
            //    strategies.push_back(localStrategy);
            //}


            //{
            //    BaseStrategy::Param baseParam{};
            //    baseParam.startCash = startCash;
            //    baseParam.subjects = subjects_sptr;
            //    baseParam.subjectCount = subjectCount;
            //    baseParam.subjectLength = subjectLength;

            //    WaterChickenStrategy::Param maGreedParam{};
            //    maGreedParam.everyDayCash = 1000.0;
            //    auto& localStrategy = std::make_shared<WaterChickenStrategy>(baseParam, maGreedParam);
            //    localStrategy->Reset();
            //    strategies.push_back(localStrategy);
            //}

            //{
            //    BaseStrategy::Param baseParam{};
            //    baseParam.startCash = startCash;
            //    baseParam.subjects = subjects_sptr;
            //    baseParam.subjectCount = subjectCount;
            //    baseParam.subjectLength = subjectLength;

            //    for (uint32_t i = 100; i < 2500; i++)
            //    {
            //        RemainStrategy::Param maGreedParam{};
            //        maGreedParam.everyDayCash = 1000.0;
            //        maGreedParam.remainDays = i;
            //        auto localStrategy = std::make_shared<RemainStrategy>(baseParam, maGreedParam);
            //        localStrategy->Reset();
            //        localStrategy->SetRecord(false);
            //        strategies.push_back(localStrategy);
            //    }
            //}


            {
                BaseStrategy::Param baseParam{};
                baseParam.startCash = startCash;
                baseParam.subjects = subjects_sptr;
                baseParam.subjectCount = subjectCount;
                baseParam.subjectLength = subjectLength;

                for (size_t i = 1; i < 1000; i++)
                {
                    for (size_t j = 1; j < 1000; j++)
                    {

                        MAGREEDStrategy::Param maGreedParam{};
                        maGreedParam.everyDayCash = 1000.0;
                        maGreedParam.baseExtraCash = 100.0 * i;
                        maGreedParam.minInvestThreadHold = 0.001 * j;
                        auto localStrategy = std::make_shared<MAGREEDStrategy>(baseParam, maGreedParam);
                        localStrategy->Reset();
                        localStrategy->SetRecord(false);
                        strategies.push_back(localStrategy);
                    }
                }
            }

            //{
            //    BaseStrategy::Param baseParam{};
            //    baseParam.startCash = startCash;
            //    baseParam.prices_sptr = prices_sptr;
            //    baseParam.subjectCount = subjectCount;
            //    baseParam.subjectLength = subjectLength;

            //    GOLDIXICStrategy::Param goldIxicParam{};
            //    goldIxicParam.everyDayCash = 1000.0;
            //    goldIxicParam.gold_rate = 0.2;
            //    goldIxicParam.ixic_rate = 0.8;
            //    goldIxicParam.refixRate_gold_to_ixic = 0.25;
            //    goldIxicParam.refixRate_ixic_to_gold = 0.9;
            //    BaseStrategy* localStrategy = new GOLDIXICStrategy(baseParam, goldIxicParam);
            //    localStrategy->Reset();
            //    strategies->push_back(localStrategy);
            //}


            // Using tester to execute all tasks, 10000000 task only need 24 seconds.
            StrategyTester tester(strategies, subjectLength);
            tester.InitAllTasks();
            tester.Execute();
            tester.ReserveNeededStrategies();
            tester.InitAllTasks();
            tester.Execute();
            tester.DumpAllFinalStrategies();
        }
    }

    return 0;
}